package com.univrz.flexmachine.scmxml.v
{
	

	import com.univrz.flexmachine.scxml.model.InitialNode;
	import com.univrz.flexmachine.scxml.model.Node;
	import com.univrz.flexmachine.scxml.model.ParallelNode;
	import com.univrz.flexmachine.scxml.model.SCXMLModelObject;
	import com.univrz.flexmachine.scxml.model.StateNode;
	import com.univrz.flexmachine.scxml.model.TransitionEdge;
	
	import flash.utils.Dictionary;
	
	import mx.core.Container;
	import mx.core.UIComponent;

	
	public class SCMXMLVUtil
	{


		public static function compile( root:State ):StateNode
		{
			process( root );
			return getModelObject( root ) as StateNode;
		}
		
		
		private static function process( comp:UIComponent ):void
		{

			getModelObject( comp );
			
			if ( !(comp is Container) )
				return;

			for ( var i:int=0; i<comp.numChildren; i++ )
				process( comp.getChildAt( i ) as UIComponent );
		}		
		
		
		private static var cache:Dictionary = new Dictionary( true );

		public static function getModelObject( comp:UIComponent ):SCXMLModelObject
		{
			if ( cache[comp] === undefined )
				cache[comp] = $getModelObject( comp );
			return cache[comp];	
		}
		
		
		/**
		 * 
		 * TODO: check well formed nesting here ( shouldn't happen because of the marker interfaces )
		 * 
		 * @param comp
		 * @return 
		 * 
		 */		
		private static function $getModelObject( comp:UIComponent ):*
		{
			var obj:*;		
			if ( comp is State )
			{
				obj = new StateNode( pNode( comp ) );
			}
			else if ( comp is Parallel )
			{
				obj = new ParallelNode( pNode( comp ) );
			}
			else if ( comp is Initial )
			{
				obj = new InitialNode( pNode( comp ) as StateNode );
			}			
			else if ( comp is Transition )
			{
				var t:Transition = comp as Transition;
				var target:* = resolveReferences( comp, [t.target] )[0];
				
				obj = new TransitionEdge( pNode( comp ), t.cond, t.event, [getModelObject( target )], null );
				
				// TODO: replace fire event with an executable content
			}
			else
			{
				return null;	// if it is not a state machine node
			}

			
			( comp as Object ).setModelObject( obj );
				
			return obj;
		
		}
		
		
		
		/**
		 * 
		 * Returns the parent model object for a given comp
		 * 
		 * @return 
		 * 
		 */		
		private static function pNode( comp:UIComponent ):Node
		{
			return getModelObject( comp.parent as UIComponent ) as Node;		
		}
		
		
		
		
		private static function resolveReferences( comp:UIComponent, refs:Array ):Array
		{
			var result:Array = [ ];
			for each ( var ref:* in refs )
			{
				if ( ref is String )
					result.push( comp.document[ref] );
				else if ( ref is UIComponent )
					result.push( ref );
			}
			return result;
		}

	}

} 